# Engine and Decision
This note covers `src/decide/*` and `src/engine/*`.
## `Decide`
**Defined in:** `src/decide/mod.rs`
The policy trait that chooses a winning candidate index.
### Signature
`fn decide(&self, candidates: &[Candidate]) -> Option<usize>`
### Role
Separates score production from winner selection.
## `DefaultDecider`
**Defined in:** `src/decide/default.rs`
The built-in decider.
### Behavior
- returns `None` on an empty candidate slice,
- otherwise returns the index of the greatest `score.score`,
- ignores `priority`,
- breaks ties by first position.
### Implication
If you want lexicographic `(priority, score)` ordering, you must inject a custom [[#Decide]] implementation.
## `EngineConfig`
**Defined in:** `src/engine/config.rs`
The resolved internal configuration bag.
### Fields
- `clock: Arc<dyn Clock>`
- `decider: Arc<dyn Decide>`
- `default_vector_backend: VectorBackend`
- `n_shards: usize`
- `embedding_slot: Option<(KindId, AttrId)>`
### Important current-state note
`default_vector_backend` and `n_shards` are present in config, but current runtime code does not meaningfully vary behavior based on them.
## `CompiledScorer`
**Defined in:** `src/engine/compiled_scorer.rs`
The engine's internal executable scorer sum-type.
### Variants
- `Predicate(Program)`
- `VectorLinear { target: Box<[f32]>, metric: VectorMetric }`
### Why it exists
This replaces trait-object dispatch on the hot path with one enum match.
## `BuildError`
**Defined in:** `src/engine/builder.rs`
The error enum returned by [[#EngineBuilder]] when engine construction fails.
### Variants
- `MissingSchema`
- `UnknownKind(String)`
- `UnknownAttr(String)`
- `AttrNotInKind { kind, attr }`
- `EmbeddingSlotNotF32Arr { kind, attr }`
### Role
This protects the engine from invalid startup-time configuration, especially embedding-slot configuration.
## `EngineBuilder<T>`
**Defined in:** `src/engine/builder.rs`
The fluent constructor for [[#Engine]].
### Important methods
- `new`
- `schema`
- `clock`
- `clock_arc`
- `decider`
- `default_vector_backend`
- `n_shards`
- `with_embedding_slot`
- `build`
### Required input
A schema is mandatory.
### Optional inputs
- custom clock,
- custom decider,
- embedding slot,
- default vector backend,
- shard count.
### Important current-state note
The builder exposes more configurability than the current runtime uses. The public API is slightly ahead of the implementation.
## `Ingested<T>`
**Defined in:** `src/engine/ingest.rs`
The result enum returned by all ingest methods.
### Variants
- `Updated`
- `Registered(ActionId)`
- `Decided(Outcome<T>)`
- `NoWinner`
- `ReloadInProgress`
- `Rejected(IngestErr)`
### Role
This is not an exception path only; it is the actual protocol of the engine.
## `Outcome<T>`
**Defined in:** `src/engine/ingest.rs`
The successful trigger outcome.
### Fields
- `action_id`
- `score: ScoreResult`
- `payload: T`
### Semantics
`payload` is already post-processed if the winning action had a post closure.
## `Engine<T>`
**Defined in:** `src/engine/engine.rs`
The main orchestrator of the crate.
### Fields
- `schema: Arc<Schema>`
- `table: Arc<LocationTable<T>>`
- `config: EngineConfig`
- `metrics: Arc<EngineMetrics>`
### Public methods
- `schema`
- `location_count`
- `reload_all`
- `upsert_location`
- `remove_location`
- `ingest_update`
- `ingest_action`
- `ingest_trigger`
- `metrics`
- `builder`
### `ingest_update` semantics
- reject if reload is in progress,
- reject if location does not exist,
- delegate to `LocationState::apply_update`,
- increment update metrics on success.
### `ingest_action` semantics
- reject if reload is in progress,
- reject if location does not exist,
- compile the scorer,
- create an [[Modules/Location State#ActionEntry]],
- insert it into the action list sorted by `end` time,
- increment registration metrics.
### `ingest_trigger` semantics
- reject if reload is in progress,
- reject if location does not exist,
- expire stale actions,
- create a [[Modules/Location State#LocationView]],
- score each live action,
- build [[Modules/Scoring Core#Candidate]] values,
- call the decider,
- clone or post-process the winning payload,
- record trigger metrics.
### Important trigger-path observations
- current eligibility is time-based and location-based, not kind-filtered,
- vector scoring requires an embedding slot configured at build time,
- missing embedding data yields `NEG_INFINITY` during vector scoring,
- metrics are recorded even when the outcome is `NoWinner`.
## Helper behavior worth knowing
### `find_by_action_id`
The engine finds the winning stored action by scanning the action list for the chosen candidate's `ActionId`. This relies on the invariant that candidates are built from the same action list.
### `__test_set_reload`
A hidden test helper that toggles the reload flag directly for integration tests.
## Summary
The engine module is where the crate's abstract pieces become one concrete state machine. It is also where the difference between **declared API semantics** and **currently enforced semantics** is most visible.